package com.emex.app.stuff.gui.model;

import com.emex.app.shared.data.Client;
import com.emex.app.shared.data.JuridicalPerson;
import com.emex.app.shared.data.OrderPayment;
import com.emex.app.shared.data.OrderPosition;
import com.emex.app.shared.data.Orders;
import com.emex.app.shared.data.User;
import com.emex.app.shared.data.enums.OrderStatus;
import com.emex.app.shared.data.wrapper.OrderStatusWrapper;
import com.emex.app.stuff.provider.AuthenticationDataProvider;
import com.emex.app.stuff.provider.ClientDataProvider;
import com.emex.app.stuff.provider.JuridicalPersonDataProvider;
import com.emex.app.stuff.provider.OrderDataProvider;
import com.emex.app.stuff.utils.StringUtilities;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import javax.swing.table.AbstractTableModel;
import javax.swing.table.TableModel;

/**
 *
 * @author Sergey Karavaev
 */
public class OrderReportTableModel extends AbstractTableModel
        implements TableModel, TableCrud<Orders> {

    private Map<Orders, List<OrderPosition>> ordersMap = new HashMap<>();

    private Map<Orders, String> clientOrderMap = new HashMap<>();

    private Map<Orders, Double> orderPurprice = new HashMap<>();

    private Map<Orders, Double> orderPrice = new HashMap<>();

    private Map<Orders, Double> orderOstatok = new HashMap<>();
    
    private List<JuridicalPerson> allJuridicPersons;

    private List<User> managers;

    private final List<Orders> orders;

    private Double genPrice = 0.0;

    private Double genPurPrice = 0.0;

    private Double genOstatok = 0.0;

    /**
     * Column names.
     */
    protected String[] columnNames = new String[]{
        "Название заказа", "ФИО клиента", "Сумма закупки", "Сумма продажи", "Прибыль",
        "% наценки", "Остаток", "Статус заказа", "Менеджер"
    };
    /**
     * Column classes
     */
    protected Class[] columnClasses = new Class[]{
        String.class, String.class, Double.class, Double.class, Double.class, Double.class, Double.class,
        OrderStatusWrapper.class, String.class
    };

    /**
     * {@inheritDoc}
     */
    @Override
    public Class getColumnClass(int index) {
        return columnClasses[index];
    }

    public OrderReportTableModel(List<Orders> ordersList) {
        if (null == ordersList) {
            ordersList = new ArrayList<>();
        }
        orders = ordersList;
        allJuridicPersons = JuridicalPersonDataProvider.getInstance().getAllJuridicalPersons();
        initializeManagers();
        initializeOrdersMap();
        calculateSums();
        calculatePricesForOrders();
    }

    private void initializeManagers() {
        managers = AuthenticationDataProvider.getInstance().getAllUsers();
    }

    private void initializeOrdersMap() {
        if (null != orders && !orders.isEmpty()) {
            for (Orders o : orders) {
                List<OrderPosition> orderPositions = OrderDataProvider.getInstance().getOrderPositionByOrderId(o.getId());
                if (null != orderPositions) {
                    ordersMap.put(o, orderPositions);
                }
                Client client = ClientDataProvider.getInstance().selectByPrimaryKey(o.getClientId());
                if (null != client) {
                    String clientFio = client.getSurname() + " "
                            + client.getName().charAt(0) + ". "
                            + client.getPatronymic().charAt(0) + ".";
                    clientOrderMap.put(o, clientFio);
                }
            }
        }
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public int getRowCount() {
        return orders.size();
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public int getColumnCount() {
        return columnNames.length;
    }

    @Override
    public Object getValueAt(int rowIndex, int columnIndex) {
        if ((rowIndex >= 0) && (rowIndex < orders.size())
                && (columnIndex >= -1) && (columnIndex < columnNames.length)) {
            switch (columnIndex) {
                case -1: {
                    return orders.get(rowIndex);
                }
                case 0: {
                    return orders.get(rowIndex).getName();
                }
                case 1: {
                    Orders o = orders.get(rowIndex);
                    if (o.getClientId() != null) {
                        return clientOrderMap.get(o);
                    } else {
                        return getJuridicPersonNameById(o.getJurClienId());
                    }
                }
                case 2: {
                    return round(orderPurprice.get(orders.get(rowIndex)), 2);
                }
                case 3: {
                    return round(orderPrice.get(orders.get(rowIndex)), 2);
                }
                case 4: {
                    return round(orderPrice.get(orders.get(rowIndex)) - orderPurprice.get(orders.get(rowIndex)), 2);
                }
                case 5: {
                    Double nacenka = orderPrice.get(orders.get(rowIndex)) - orderPurprice.get(orders.get(rowIndex));
                    if (nacenka != 0.0) {
                        return round((nacenka / orderPurprice.get(orders.get(rowIndex)) * 100), 2);
                    } else {
                        return 0.0;
                    }
                }
                case 6: {
                    return round(orderOstatok.get(orders.get(rowIndex)), 2);
                }
                case 7: {
                    return new OrderStatusWrapper(OrderStatus.valueOf(orders.get(rowIndex).getStatus()), ordersMap.get(orders.get(rowIndex)));
                }
                case 8: {
                    return getManagerFIOById(orders.get(rowIndex).getManagerId());
                }
                default:
                    return null;
            }
        }
        return null;
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public String getColumnName(int column) {
        return columnNames[column];
    }

    @Override
    public void dataRemoved(Orders t) {
        orders.remove(t);
        this.fireTableDataChanged();
    }

    @Override
    public void dataAdded(Orders t) {
        orders.add(t);
        this.fireTableDataChanged();
    }

    @Override
    public void dataUpdated() {
        this.fireTableDataChanged();
    }

    private void calculatePricesForOrders() {
        for (Orders o : orders) {
            Double purPrice = 0.0;
            Double price = 0.0;
            Double ostatok = 0.0;
            List<OrderPosition> orderPositions = ordersMap.get(o);
            if (null != orderPositions && !orderPositions.isEmpty()) {
                for (OrderPosition op : orderPositions) {
                    purPrice += (op.getPurPrice() * op.getAmount());
                    price += (op.getPrice() * op.getAmount());
                }
            }
            List<OrderPayment> orderPayments = OrderDataProvider.getInstance().getOrderPaymentByOrderId(o.getId());
            if (null != orderPayments && !orderPayments.isEmpty()) {
                for (OrderPayment op : orderPayments) {
                    ostatok += op.getSumma();
                }
            }
            genOstatok += (price - ostatok);
            orderPrice.put(o, price);
            orderPurprice.put(o, purPrice);
            orderOstatok.put(o, (price - ostatok));
        }

    }

    private void calculateSums() {
        for (Orders o : ordersMap.keySet()) {
            List<OrderPosition> orderPositions = ordersMap.get(o);
            if (null != orderPositions && !orderPositions.isEmpty()) {
                for (OrderPosition op : orderPositions) {
                    genPurPrice += (op.getPurPrice() * op.getAmount());
                    genPrice += (op.getPrice() * op.getAmount());
                }
            }
        }
    }

    public Double getGenPrice() {
        return genPrice;
    }

    public Double getGenPurPrice() {
        return genPurPrice;
    }

    private String getManagerFIOById(Integer managerId) {
        for (User user : managers) {
            if (Objects.equals(user.getId(), managerId)) {
                return StringUtilities.getManagerFIO(user);
            }
        }
        return "";
    }
    
    private String getJuridicPersonNameById(Integer jurPersonId) {
        for (JuridicalPerson jp: allJuridicPersons) {
            if (Objects.equals(jp.getId(), jurPersonId)) {
                return jp.getName();
            }
        }
        return "";
    }

    public double round(double value, int places) {
        long factor = (long) Math.pow(10, places);
        value = value * factor;
        long tmp = Math.round(value);
        return (double) tmp / factor;
    }

    public Double getGenOstatok() {
        return genOstatok;
    }

    public void setGenOstatok(Double genOstatok) {
        this.genOstatok = genOstatok;
    }
}
